Skip to content

Fix persistent browser Keychain access in desktop onboarding#9427

Open
skanderkaroui wants to merge 8 commits into
BasedHardware:mainfrom
skanderkaroui:fix/keychain-inprocess-safe-storage
Open

Fix persistent browser Keychain access in desktop onboarding#9427
skanderkaroui wants to merge 8 commits into
BasedHardware:mainfrom
skanderkaroui:fix/keychain-inprocess-safe-storage

Conversation

@skanderkaroui

@skanderkaroui skanderkaroui commented Jul 11, 2026

Copy link
Copy Markdown

What changed and why

Replace browser Safe Storage reads through /usr/bin/security with an exact in-process Security framework lookup. Keychain prompts and Always Allow grants now belong to the signed Omi app and persist across relaunches. Unsupported versioned cookie blobs fail closed, and the durable Google OAuth migration path is documented.

Product invariants affected

None.

How it was verified

  • Focused BrowserGoogleSessionTests: 4 passed, 0 failed.
  • Debug Swift package build passed.
  • Full repository pre-push checks passed, including a clean desktop rebuild.
  • Built and signed /Applications/omi-keychain-fix.app. The first browser Keychain prompt was owned by omi-keychain-fix; after choosing Always Allow and fully relaunching, gmail_read_probe returned connected and readable without another prompt.
  • Rebased onto current origin/main without conflicts. None of the intervening upstream commits touched the changed Keychain files.

Tests

BrowserGoogleSessionTests covers the complete service/account mapping for every known browser target, exact Security query construction, and behavioral rejection of unsupported versioned cookie ciphertext. Adding a future browser target without a Keychain identity causes the mapping test to fail.

Scoped cleanups (optional)

Removed the /usr/bin/security read fallback and its retry policy. This keeps one native code path and prevents future prompt attribution regressions. The branch history uses one changed file per commit.

Review in cubic

…hem as garbage

The Chromium cookie decrypt handled v10/v11 (macOS AES-128-CBC) and treated
everything else as plaintext. A v20 (app-bound) or newer encrypted value is not
plaintext, so decoding it as Latin-1 produced a garbage string that got sent as
a bogus Google auth cookie. As browsers move past v10 this silently corrupts the
import.

Guard the plaintext branch: any 'v<dd>' tag we don't decrypt (v20, v24, ...) is
skipped, not garbage-decoded. Genuinely unencrypted values still pass through.

Deliberately minimal (ponytail): no v20/app-bound decoder and no per-version
telemetry — on macOS today cookies are v10, and the durable path for newer
schemes is Google OAuth, not a per-version scraper. Ceiling noted in-code.

Test: xcrun swift build -c debug (Build complete, exit 0).
Scopes the migration away from the version-fragile browser cookie scraper to
backend-mediated Google OAuth. Reuse-first: the backend already ships the OAuth
flow, token refresh, connector pattern, and a live Google Calendar integration,
so the work is mostly wiring the desktop fetch layer to it.

Decisions captured: Calendar first (cheap/verified scope), Gmail via
gmail.readonly (accepting restricted-scope/CASA cost), backend-mediated,
standalone connector (not incremental-auth). Explicit ponytail cuts: no new
token-encryption scheme, no MCP-style multi-client OAuth, keep the fixed cookie
path as fallback.
…external blockers

calendar_tools.py already fetches events from the Google Calendar API with the
stored OAuth token, so Phase 0 is a thin read endpoint + desktop wiring, not new
OAuth. Also record the external gates (Google client creds, connected test
account, consent-screen scope config, Gmail CASA) that prevent live verification
from the repo alone.
@Git-on-my-level Git-on-my-level added security-review Touches auth, provider routing, secrets, or security-sensitive surfaces docs-accuracy Documentation or committed reports need accuracy fixes needs-maintainer-review Needs human maintainer review before merge labels Jul 11, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the careful write-up and tests here. I did a maintainer/security-focused review of the diff.

Positive signal on the code direction: moving the Keychain read in-process, matching the full Safe Storage service/account identity, avoiding a /usr/bin/security fallback, and skipping unknown versioned cookie blobs all look like the right shape for reducing confusing prompts and failing closed instead of emitting garbage cookie data. The focused tests cover the important mapping/query/regression cases.

I’m not formally approving this because it touches a security/privacy-sensitive onboarding path and adds a developer migration plan that can steer future AI/coding agents. Human maintainer review is still warranted before merge, especially for:

  • confirming the in-process Keychain prompt / “Always Allow” persistence behavior on the signed Omi desktop app across supported macOS/browser combinations;
  • validating the new OAuth migration document as maintainer-approved direction, since it describes “decisions locked,” Gmail restricted-scope/CASA tradeoffs, backend token storage assumptions, and the intended fallback strategy;
  • deciding whether this docs plan belongs in this PR or should land separately after product/security sign-off.

No urgent security/supply-chain issue found in the diff. My recommendation is positive but maintainer-gated: treat the implementation as promising, keep the security/docs review labels, and have a human maintainer validate the Keychain behavior and OAuth roadmap language before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs-accuracy Documentation or committed reports need accuracy fixes needs-maintainer-review Needs human maintainer review before merge security-review Touches auth, provider routing, secrets, or security-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants